#! /usr/bin/env python
#coding=utf-8

import os
import sys
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../"))

from utils import ResterHelper
from utils import ResterStringBuilder

import graphviz

class ElfModuleQuerier(object):
	def __init__(self, keys):
		self._keys = keys

	def get_keys(self, xargs=None):
		return tuple(self._keys)

	def match(self, key):
		if key in self._keys:
			return True
		return False

	def query(self, elf, key, cursor, xargs):
		return None

class ElfModuleSymbolQuerier(ElfModuleQuerier):
	QUERY_KEYS = ( "provided", "used", "unused", "needed", "matched", "duplicated", "unmatched" )
	QUERY_METHODS = ( "getProvidedSymbols", "getProvidedSymbolsUsed", "getProvidedSymbolsUnused",
					  "getUndefinedSymbols", "getUndefinedSymbolsMatched", "getUndefinedSymbolsDuplicated", "getUndefinedSymbolsUnmatched" )

	def __init__(self):
		super(ElfModuleSymbolQuerier, self).__init__(ElfModuleSymbolQuerier.QUERY_KEYS)

	def query(self, elf, key, cursor, xargs):
		idx = self._keys.index(key)
		symbols = elf.callByFunctionName(ElfModuleSymbolQuerier.QUERY_METHODS[idx], cursor)

		return ResterHelper.build_array_content(symbols, xargs)

class ElfModuleDepthQuerier(ElfModuleQuerier):
	QUERY_KEYS = ("depth", "dependedBy_depth" )

	def __init__(self):
		super(ElfModuleDepthQuerier, self).__init__(ElfModuleDepthQuerier.QUERY_KEYS)

	def get_keys(self, xargs=None):
		return ()

	def query(self, elf, key, cursor, xargs):
		if graphviz.requestingGraphviz(xargs):
			return elf.plotGraph(xargs)
		return ""

class ElfModuleGroupInfoQuerier(ElfModuleQuerier):
	QUERY_KEYS = ("third_party", "sa_id", "napi", "chipset", "hdiType", "innerapi", "platformsdk", "chipsetsdk" )

	def __init__(self):
		super(ElfModuleGroupInfoQuerier, self).__init__(ElfModuleGroupInfoQuerier.QUERY_KEYS)

class ElfModuleSizeInfoQuerier(ElfModuleQuerier):
	QUERY_KEYS = ("size", "bss_size", "data_size", "processes", "Pss", "swapPss", "Shared_Dirty", "Private_Dirty" )

	def __init__(self):
		super(ElfModuleSizeInfoQuerier, self).__init__(ElfModuleSizeInfoQuerier.QUERY_KEYS)

class IndirectDependencyRest(ResterStringBuilder):
	def __init__(self, indirect):
		for k, v in indirect.items():
			self[k] = v

	def getStr(self, xargs=None, keys=None):
		sorted_keys = ("id", "caller.id", "callee.id", "caller.componentName", "caller.name", "callee.componentName", "callee.name", "depType", "calls")
		return ResterStringBuilder.getStr(self, xargs, sorted_keys)

class ElfModuleDepsQuerier(ElfModuleQuerier):
	QUERY_KEYS = ("deps", "deps_external", "deps_internal", "deps_indirect", "deps_total", "dependedBy", "dependedBy_external", "dependedBy_internal", "dependedBy_indirect", "dependedBy_total")

	def __init__(self):
		super(ElfModuleDepsQuerier, self).__init__(ElfModuleDepsQuerier.QUERY_KEYS)

	def _get_deps_indirect(self, mod):
		vals = []
		for indirect in mod.getDepsIndirect():
			vals.append(IndirectDependencyRest(indirect))
		return vals

	def _get_dependedBy_indirect(self, mod):
		vals = []
		for indirect in mod.getDependedByIndirect():
			vals.append(IndirectDependencyRest(indirect))
		return vals

	def _get_deps_with_indirect_aware(self, mod, key):
		if key in ("deps_indirect", "deps_total"):
			indirect = self._get_deps_indirect(mod)
			if key == "deps_total":
				return mod["deps"] + indirect
			return indirect

		if key in ("dependedBy_indirect", "dependedBy_total"):
			indirect = self._get_dependedBy_indirect(mod)
			if key == "dependedBy_total":
				return indirect + mod["dependedBy"]
			return indirect

		if key in ("deps_external", "deps_internal"):
			external = (key == "deps_external")
			return [m for m in mod["deps"] if m["external"] == external]

		if key in ("dependedBy_external", "dependedBy_internal"):
			external = (key == "dependedBy_external")
			return [m for m in mod["dependedBy"] if m["external"] == external] 

		return mod[key]

	def get_keys(self, xargs=None):
		return ("depth", ) + ElfModuleDepsQuerier.QUERY_KEYS + ("dependedBy_depth", )

	def query(self, elf, key, cursor, xargs):
		vals = self._get_deps_with_indirect_aware(elf, key)
		if graphviz.requestingGraphviz(xargs):
			return elf.plotGraph(xargs)
		return ResterHelper.build_array_content(vals, xargs)

class SdkDepsQuerier(ElfModuleQuerier):
	QUERY_KEYS = ("chipsetsdk_dependedBy", "platformsdk_dependedBy" )

	def __init__(self):
		super(SdkDepsQuerier, self).__init__(SdkDepsQuerier.QUERY_KEYS)

	def _get_chipsetsdk_dependedBy(self, elf):
		pass

	def query(self, elf, key, cursor, xargs):
		vals = []
		if key == "chipsetsdk_dependedBy":
			vals = elf.getChipsetSdkDependedBy()
		if graphviz.requestingGraphviz(xargs):
			return elf.plotGraph(xargs)
		return ResterHelper.build_array_content(vals, xargs)

class SdkSymbolsQuerier(ElfModuleQuerier):
	QUERY_KEYS = ("chipsetsdk_symbols", "platformsdk_symbols", "external_symbols" )
	QUERY_METHODS = ( "getChipsetSdkSymbols", "getPlatformSdkSymbols", "getExternalSymbols")

	def __init__(self):
		super(SdkSymbolsQuerier, self).__init__(SdkSymbolsQuerier.QUERY_KEYS)

	def query(self, elf, key, cursor, xargs):
		idx = self._keys.index(key)
		symbols = elf.callByFunctionName(SdkSymbolsQuerier.QUERY_METHODS[idx], cursor)

		return ResterHelper.build_array_content(symbols, xargs)

class HiddenFieldsQuerier(ElfModuleQuerier):
	QUERY_KEYS = ("path", "labelPath", "shlib_type", "innerapi_tags", "right_tags", "chipsetsdk_indirect", "platformsdk_indirect", "innerapi_declared", "version_script")

	def __init__(self):
		super(HiddenFieldsQuerier, self).__init__(HiddenFieldsQuerier.QUERY_KEYS)

class ElfModuleInfoQuerierMgr(object):
	QUERIERS = ( ElfModuleDepsQuerier(), ElfModuleDepthQuerier(), ElfModuleSymbolQuerier(), ElfModuleSizeInfoQuerier(), ElfModuleGroupInfoQuerier() )
	HIDDEN_QUERIERS = (SdkDepsQuerier(), SdkSymbolsQuerier(), HiddenFieldsQuerier())

	@staticmethod
	def get_all_keys(xargs=None):
		res = ()
		queriers = ElfModuleInfoQuerierMgr.QUERIERS
		if xargs and "format" in xargs and xargs["format"] in ("xml", "json"):
			queriers = queriers + ElfModuleInfoQuerierMgr.HIDDEN_QUERIERS
		for q in queriers:
			res = res + q.get_keys(xargs)
		return res

	@staticmethod
	def query(elf, key, cursor, xargs):
		for q in ElfModuleInfoQuerierMgr.QUERIERS + ElfModuleInfoQuerierMgr.HIDDEN_QUERIERS:
			if not q.match(key):
				continue
			return q.query(elf, key, cursor, xargs)

		return ""
